home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Astronomy
/
Moon
/
Source
/
Controller.m
< prev
next >
Wrap
Text File
|
1993-01-19
|
12KB
|
390 lines
/* Controller.m
* Part of the Moon application for the NeXT computer.
* Author: Geoffrey S. Knauth
* Date: January 4, 1992
*
* Permission to copy this program is hereby granted under the terms
* of the Free Software Foundation's GNU General Public License.
*/
/* Initially generated by Interface Builder */
#import <strings.h> /* strcpy */
#import <time.h> /* struct tm, gmtime, etc. */
#import <dpsclient/dpsclient.h> /* DPSAddTimedEntry */
#import <appkit/Application.h> /* NX_BASETHRESHOLD */
#import <appkit/Font.h> /* NX_IDENTITYMATRIX */
#import <appkit/publicWraps.h> /* NXBeep */
#import <objc/NXStringTable.h>
#import "all.h"
#import "Controller.h"
#import "MoonView.h"
#import "MoonIconView.h"
static DPSTimedEntry te = 0;
void tick(DPSTimedEntry teNumber, double now, void *userData);
@implementation Controller
- appDidHide :sender
{
[iconView display];
return self;
}
- appDidInit:sender
{
NXRect iconRect = {{0.0, 0.0}, {64.0, 64.0}};
long t; /* seconds since 0000 UTC 1/1/1970 */
struct tm *tmLcl;
char buf[80];
nextNewMoon = 0.0; /* invalidate new moon info */
/* make our icon view be the appIcon window's contentView */
iconView = [[MoonIconView alloc] initFrame:&iconRect];
[[[NXApp appIcon] setContentView:iconView] free];
/* The nowButton should not be enabled until the user has gone into
* time travel mode.
*/
[nowButton setEnabled:NO];
[statForm setTextFont:
[Font newFont:"Ohlfs" size:10.0 matrix:NX_FLIPPEDMATRIX]];
/* The local time title must be big enough to hold "Local (GMT+11:30)". */
(void) time(&t);
tmLcl = localtime(&t);
sprintf(buf, "Local (%s)", tmLcl->tm_zone);
[statForm setTitle:buf at:iLocalTime];
/* Make sure the function tick (which sends us an update message)
* is called on a regular basis.
*/
te = DPSAddTimedEntry(TICK_SECONDS, tick, self, NX_BASETHRESHOLD);
[self update]; /* update now, in case the wait is long */
return self;
}
- appWillTerminate :sender
{
DPSRemoveTimedEntry(te);
return self;
}
- getUserGmtTime :(int *)year :(int *)month :(int *)day
:(int *)hour :(int *)minute :(int *)second
{
char buf[80], monthStr[20];
int i, n, expected, hhmm;
/* Here are the kinds of input we can accept:
* hh:mm:ss dd mmmmm yyyy %d:%d:%d %d %s %d (6)
* hh:mm dd mmmmm yyyy %d:%d %d %s %d (5)
* hhmm dd mmmmm yyyy %d %d %s %d (4)
* dd mmmmm yyyy %d %s %d (3)
* mmmmm yyyy %s %d (2)
* yyyy %d (1)
*/
strcpy(buf, [travelText stringValue]);
for (i = n = 0; i < 6; n = 0, i++) {
expected = *hour = *minute = *second = *day = *month = *year = 0;
switch (i) {
case 0:
expected = 6;
n = sscanf(buf, "%d:%d:%d %d %s %d",
hour, minute, second, day, monthStr, year);
break;
case 1:
expected = 5;
n = sscanf(buf, "%d:%d %d %s %d",
hour, minute, day, monthStr, year);
break;
case 2:
expected = 4;
n = sscanf(buf, "%d %d %s %d", &hhmm, day, monthStr, year);
break;
case 3:
expected = 3;
n = sscanf(buf, "%d %s %d", day, monthStr, year);
break;
case 4:
expected = 2;
n = sscanf(buf, "%s %d", monthStr, year);
*day = 1;
break;
case 5:
expected = 1;
n = sscanf(buf, "%d", year);
*day = 1;
strcpy(monthStr, [stringTable valueForStringKey:"1"]);
break;
}
if (n && n == expected) break;
}
if (n) {
*month = [self monthNumFromStr:monthStr];
if (*month == 0) goto bad;
if (expected == 4) {
*hour = hhmm / 100;
*minute = hhmm % 100;
}
return self;
}
bad:
*hour = *minute = *second = *day = *month = *year = 0;
return nil;
}
- (int)monthNumFromStr :(const char *)month
{
char monthBuf[2+1];
int i;
for (i = 12; i > 0; i--) {
sprintf(monthBuf, "%d", i);
if (strcmp(month, [stringTable valueForStringKey:monthBuf]) == 0)
break;
}
return i;
}
- pause :sender
{
/* We need a pause method, so that when we are coming out of pause,
* resuming to run, we can generate an update right away, without
* making the user wait for the next official tick.
*/
if ([sender state] == RUNNING) /* state changed before action sent */
[self update];
return self;
}
- returnToNow :sender
{
/* When the nowButton is disabled, we are not in time travel mode.
* When are not in time travel mode, update gets its time from the system,
* not from the user.
*/
[sender setEnabled:NO]; /* sender is also nowButton */
[pauseButton setEnabled:YES]; /* by definition we will run */
nextNewMoon = 0.0; /* invalidate new moon info */
[self update];
return self;
}
- timeTravel :sender
{
/* When the nowButton is enabled, we are in time travel mode.
* When are in time travel mode, update gets its time from the user,
* not from the system.
*/
[nowButton setEnabled:YES];
[pauseButton setEnabled:NO]; /* by definition we will pause */
nextNewMoon = 0.0; /* invalidate new moon info */
[self update];
return self;
}
- update
{
long t; /* seconds since 0000 UTC 1/1/1970 */
static long gmtoff = -1L; /* timezone offset from UTC in secs */
struct tm *tmUtc, *tmLcl; /* Unix UTC & local time structures */
float p; /* moon phase: 0=new, 0.5=full */
int lunation;
double jd, aom, cphase, cdist, cangdia, csund, csuang, lptime;
double phasar[5];
double fakeJd; /* don't use for astro calculations! */
char tbuf[80];
char monthBuf[2+1]; /* "1" .. "12" */
int yy, mm, dd, hh, mmm, ss; /* moon times */
int year, month, day; /* our UTC times */
int hour, minute, second; /* our UTC times */
int lyear, lmonth, lday; /* our local times */
int lhour, lminute, lsecond; /* our local times */
if ([pauseButton state] == PAUSED) return nil;
if ([nowButton isEnabled]) {
/* We're traveling in time.
* This is not the initial state, so gmtoff is legitimate.
*/
if ([self getUserGmtTime
:&year :&month :&day :&hour :&minute :&second] == nil)
{
NXBeep();
[nowButton setEnabled:NO]; /* no time travel after all */
return nil;
}
jd = ymdhmsToJtime(year, month, day, hour, minute, second);
fakeJd = jd + (double)gmtoff / 86400.;
} else {
/* We're not traveling in time. Unix gives us our time info.
* This is the initial state.
*/
(void) time(&t);
tmUtc = gmtime(&t);
jd = jtime(tmUtc);
year = tmUtc->tm_year + 1900;
month = tmUtc->tm_mon + 1;
day = tmUtc->tm_mday;
hour = tmUtc->tm_hour;
minute = tmUtc->tm_min;
second = tmUtc->tm_sec;
tmLcl = localtime(&t);
fakeJd = jtime(tmLcl);
if (gmtoff == -1L) {
/* Save the time zone offset for later. We'll need it if the user
* does any time traveling, because if we go too far into the past
* or future, Unix time functions won't help us any more.
*/
gmtoff = tmLcl->tm_gmtoff; /* remember offset is in seconds */
}
}
p = (float)jdtophase(jd, &cphase, &aom, &cdist, &cangdia, &csund, &csuang);
/* Draw the big image of the moon and shadow it. */
[moonView setPhase:p];
[moonView display];
/* Draw the little icon image of the moon and shadow it. */
[iconView setPhase:p];
[iconView display];
/* Update textual information */
sprintf(tbuf, "%.5f", jd); /* 1979 January 1.0 <==> JD 2443874.5 */
[statForm setStringValue:tbuf at:iJulianDate];
sprintf(monthBuf, "%d", month);
sprintf(tbuf, "%02d:%02d:%02d %d %s %d",
hour, minute, second, day,
[stringTable valueForStringKey:monthBuf], year);
[statForm setStringValue:tbuf at:iUniversalTime];
jyear(fakeJd, &lyear, &lmonth, &lday);
jhms(fakeJd, &lhour, &lminute, &lsecond);
sprintf(monthBuf, "%d", lmonth);
sprintf(tbuf, "%02d:%02d:%02d %d %s %d",
lhour, lminute, lsecond, lday,
[stringTable valueForStringKey:monthBuf], lyear);
[statForm setStringValue:tbuf at:iLocalTime];
sprintf(tbuf, "%d%% %s, %s",
(int) rint(cphase * 100),
[stringTable valueForStringKey:"visible"],
p < 0.5 ?
[stringTable valueForStringKey:"waxing"]
: [stringTable valueForStringKey:"waning"]);
[statForm setStringValue:tbuf at:iMoonPhase];
/* Some information about the Moon */
sprintf(tbuf, "%dd %dh %dm",
(int) aom, (int) (24 * (aom - floor(aom))),
((int) (1440 * (aom - floor(aom)))) % 60);
[statForm setStringValue:tbuf at:iAgeOfMoon];
sprintf(tbuf, "%ld km, %.1f %s",
(long) cdist, cdist / earthrad,
[stringTable valueForStringKey:"earthRadii"]);
[statForm setStringValue:tbuf at:iMoonDistance];
sprintf(tbuf, "%.4f %s", cangdia,
[stringTable valueForStringKey:"degrees"]);
[statForm setStringValue:tbuf at:iMoonSubtends];
/* Information about the Sun */
sprintf(tbuf, "%.0f km, %.3f AU", csund, csund / sunsmax);
[statForm setStringValue:tbuf at:iSunDistance];
sprintf(tbuf, "%.4f %s", csuang,
[stringTable valueForStringKey:"degrees"]);
[statForm setStringValue:tbuf at:iSunSubtends];
/* Calculate times of phases of this lunation. This is sufficiently
* time-consuming that we only do it once a month, or when we begin or
* return from time travel. If we've just changed the time travel mode,
* nextNewMoon will be zero.
*/
if (jd > nextNewMoon) {
phasehunt(jd, phasar);
lptime = phasar[0];
lunation = floor(((lptime + 7) - lunatbase) / synmonth) + 1;
jyear(lptime, &yy, &mm, &dd);
jhms(lptime, &hh, &mmm, &ss);
sprintf(monthBuf, "%d", mm);
sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
[stringTable valueForStringKey:monthBuf], yy);
[statForm setStringValue:tbuf at:iLastNewMoon];
sprintf(tbuf, "%d", lunation);
[statForm setStringValue:tbuf at:iThisLunation];
lptime = phasar[1];
jyear(lptime, &yy, &mm, &dd);
jhms(lptime, &hh, &mmm, &ss);
sprintf(monthBuf, "%d", mm);
sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
[stringTable valueForStringKey:monthBuf], yy);
[statForm setStringValue:tbuf at:iFirstQuarter];
lptime = phasar[2];
jyear(lptime, &yy, &mm, &dd);
jhms(lptime, &hh, &mmm, &ss);
sprintf(monthBuf, "%d", mm);
sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
[stringTable valueForStringKey:monthBuf], yy);
[statForm setStringValue:tbuf at:iFullMoon];
lptime = phasar[3];
jyear(lptime, &yy, &mm, &dd);
jhms(lptime, &hh, &mmm, &ss);
sprintf(monthBuf, "%d", mm);
sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
[stringTable valueForStringKey:monthBuf], yy);
[statForm setStringValue:tbuf at:iLastQuarter];
nextNewMoon = phasar[4];
jyear(nextNewMoon, &yy, &mm, &dd);
jhms(nextNewMoon, &hh, &mmm, &ss);
sprintf(monthBuf, "%d", mm);
sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
[stringTable valueForStringKey:monthBuf], yy);
[statForm setStringValue:tbuf at:iNextNewMoon];
sprintf(tbuf, "%d", lunation + 1);
[statForm setStringValue:tbuf at:iNextLunation];
}
return self;
}
void tick(DPSTimedEntry teNumber, double now, void *userData)
{
Controller *self = userData; /* this trick lets C do some Obj-C */
/* We only came here because of the interface from the DPSTimedEntry.
* Now let's be more objective.
*/
if ([self->nowButton isEnabled]) {
/* We're in time travel mode.
* That means only the user should be able to generate update events.
* While in this mode, ticks will be ignored.
* We do this so that the user may finish typing a time travel date
* and tell us when to read it, otherwise we are likely to read it
* prematurely.
*/
return;
}
[self update];
}
@end